package MusicLandscape.util.io;

import MusicLandscape.entities.Artist;
import MusicLandscape.entities.Track;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.Scanner;
import java.util.regex.Pattern;

/**
 * Class for reading a Track from a CSV input.
 * <p>
 * The order of the Track fields in the CSV line must be:
 *
 * <ol>
 *     <li>Title</li>
 *     <li>Writer</li>
 *     <li>Performer</li>
 *     <li>Duration</li>
 *     <li>Year</li>
 * </ol>
 *
 * @author Jonas Altrock (ew20b126@technikum-wien.at)
 * @version 1
 * @since ExerciseSheet06
 */
public class MyTrackCSVReader extends MyReader<Track> {
    /**
     * CSV field delimiter.
     */
    private static final String delimiter = ",|$";

    /**
     * Regular expression pattern for a quoted string field.
     */
    private static final Pattern quotedString = Pattern.compile("\"(\\\\\"|[^\"])*?\"");

    /**
     * Regular expression for whitespace.
     */
    private static final Pattern whitespace = Pattern.compile("\\p{javaWhitespace}*");

    /**
     * constructs a MyCSVTrackReader from a Buffered Reader.<br>
     * the underlying stream cannot be null. In case a null object is passed to
     * this constructor an IllegalArgumentException including
     * a custom message "expected non-null ReaderObject" is thrown.
     *
     * @param in the underlying stream
     */
    public MyTrackCSVReader(BufferedReader in) {
        super(in);
    }

    /**
     * reads the current line of the BufferedReader, displays it as is at the console and returns the contained Track
     * of this line<br>
     * displays "Error reading." in case of an IOException<br>
     * displays "Error parsing." in case of any other exception
     *
     * @return Track in case a new Track was created successfully, null otherwise
     */
    @Override
    public Track get() {
        String line = "";

        try {
            line = in.readLine();

            if (line == null || line.isEmpty()) {
                return null;
            }

            Scanner scan = new Scanner(line).useDelimiter(delimiter);

            Track t = new Track();
            t.setTitle(nextMaybeQuoted(scan));
            t.setWriter(new Artist(nextMaybeQuoted(scan)));
            t.setPerformer(new Artist(nextMaybeQuoted(scan)));
            t.setDuration(nextInt(scan));
            t.setYear(nextInt(scan));
            return t;
        } catch (IOException e) {
            System.out.println("Error reading.");
        } catch (Exception e) {
            System.out.println(line + "Error parsing.");
        }

        return null;
    }

    /**
     * Read a possibly quoted string field from the scanner.
     *
     * @param s the scanner to use
     * @return the text of the next field
     */
    private String nextMaybeQuoted(Scanner s) {
        String r = null;
        // imposed by MyWriterTest: skip leading whitespace
        s.skip(whitespace);

        if (s.hasNext("\\\".*")) {
            r = s.findInLine(quotedString);

            if (r != null) {
                r = r.substring(1, r.length() - 1).replaceAll("\\\\\"", "\"");
            }
        } else if (s.hasNext()) {
            r = s.next().trim();
        }

        s.skip(s.delimiter());
        return r;
    }

    /**
     * Read an integer field.
     *
     * @param s the scanner to use
     * @return the next field as an integer
     */
    private int nextInt(Scanner s) {
        // imposed by MyWriterTest: skip leading whitespace
        s.skip(whitespace);
        int r = s.nextInt();
        s.skip(s.delimiter());
        return r;
    }
}
